/*  File Name	: MakeMsgBlock.cpp
 *  Author		: Pang Jiacai
 *	 E-mail		: 823960168@qq.com
 *  Created on	: 2012-7-4
 *  Description:
 */

#include "MakeMsgBlock.h"
#include <stdlib.h>
#include "Logger.h"

//#define MEMZERO(PTR,LEN) memset(PTR,'\0',LEN)

MakeMsgBlock::MakeMsgBlock(int space_size)
{
	this->endian_ = MessageBlock::endian();
	this->size_ = space_size;
	this->base_ptr_ = NULL;
	this->read_ptr_ = NULL;
	this->write_ptr_ = NULL;
	this->have_left_msg_ = false;
	this->storage_len_ = 0;
	this->next_msg_size_ = 0;

	if(this->size_>0)
	{
		this->init(this->size_);
	}
}

MakeMsgBlock::~MakeMsgBlock()
{
	if(NULL!=this->base_ptr_)
	{
		delete [] this->base_ptr_;
		this->base_ptr_ = NULL;
	}
}

void MakeMsgBlock::reset_space(void)
{
	MEMZERO(this->base_ptr_,this->size_);

	this->read_ptr_ = this->base_ptr_;
	this->write_ptr_ = this->base_ptr_;
	this->have_left_msg_ = false;
	this->storage_len_ = 0;
	this->next_msg_size_ = 0;
}

int MakeMsgBlock::init(int space_size)
{
	if(space_size >0)
	{
		this->size_ = space_size;
	}
	if(this->size_<=0 || this->base_ptr_!=NULL)
	{
		return -1;
	}

	if (NULL == this->base_ptr_)
	{
		this->base_ptr_ = new char[this->size_];
		if (NULL == this->base_ptr_)
		{
			return -1;
		}
	}

	this->reset_space();

	return 0;
}

int MakeMsgBlock::space_size(void)
{
	return this->size_;
}

char *MakeMsgBlock::write_ptr(void)
{
	return this->write_ptr_;
}

char *MakeMsgBlock::read_ptr(void)
{
	return this->read_ptr_;
}

int MakeMsgBlock::left_space(void)
{
	return this->size_ - this->storage_len_;
}

int MakeMsgBlock::next_msg_size(void)
{
	return this->next_msg_size_ ;
}

/**
 * return
 *  2: success and have next  block
 *  1: success and have one block
 *  0: success but no message
 * -1: mb error
 * -2: space error,should close socket
 */
int MakeMsgBlock::get_block(MessageBlock *mb)
{
	if(NULL==mb)
	{
		return -1;
	}
	int int_size = sizeof(int);
	if((this->base_ptr_+this->size_-this->read_ptr_)<int_size)
	{
		int cur_size = this->base_ptr_+this->size_-this->read_ptr_;
		MessageBlock tmp(int_size*2);
		::memcpy(tmp.wr_ptr(),this->read_ptr_,cur_size);
		tmp.wr_ptr(cur_size);
		this->read_ptr_ = this->base_ptr_;
		int left_size = int_size-cur_size;
		::memcpy(tmp.wr_ptr(),this->read_ptr_,left_size);
		tmp.wr_len(int_size);

		if (this->endian_ == ENDIAN_LITTLE)
		{
			char *p = (char*) &this->next_msg_size_;
			int char_size = sizeof(char);
			for (int i = 0; i < int_size; i++)
			{
				memcpy(p + i * char_size,tmp.rd_ptr() + (int_size - 1 - i) * char_size, char_size);
			}
		}
		else
		{
			this->next_msg_size_ = *((int*)tmp.rd_ptr());
		}
		this->read_ptr_ +=left_size;
	}
	else
	{
		if(this->endian_==ENDIAN_LITTLE)
		{
			char *p = (char*) &this->next_msg_size_;
			int char_size = sizeof(char);
			for (int i = 0; i < int_size; i++)
			{
				memcpy(p + i * char_size,this->read_ptr_ + (int_size - 1 - i) * char_size, char_size);
			}
		}
		else
		{
			this->next_msg_size_ = *((int*)this->read_ptr_);
		}
		this->read_ptr_ += int_size;
	}

	if(this->next_msg_size_<=0 && this->next_msg_size_>=4096)
	{
		return -2;
	}

	int data_len = this->next_msg_size_-int_size;

	LOG((DEBUG_LOG,"MakeMsgBlock::get_block [%d][%d]",this->storage_len_,this->next_msg_size_));
	if(this->storage_len_>=next_msg_size_)
	{
		if(NULL==mb || mb->size()<this->next_msg_size_)
		{
			return -1;
		}

		bool flag = false;
		if(this->base_ptr_+this->size_-this->read_ptr_< data_len)
		{
			flag = true;
		}
		if(!flag)
		{
			memcpy(mb->wr_ptr(),this->read_ptr_,data_len);
			MEMZERO(this->read_ptr_,data_len);
			this->read_ptr_ += data_len;
		}
		else
		{
			int left_len = this->base_ptr_+this->size_-this->read_ptr_;
			memcpy(mb->wr_ptr(),this->read_ptr_,left_len);
			MEMZERO(this->read_ptr_,left_len);

			mb->wr_ptr(left_len);

			this->read_ptr_ = this->base_ptr_;
			memcpy(mb->wr_ptr(),this->read_ptr_,data_len-left_len);
			MEMZERO(this->read_ptr_,data_len-left_len);
			this->read_ptr_ +=(data_len-left_len);
			mb->wr_ptr(data_len-left_len);
		}
		mb->wr_len(data_len);

		this->storage_len_ -=next_msg_size_;

		this->next_msg_size_ = *((int*)this->read_ptr_);
		if(this->next_msg_size_ >0 && this->next_msg_size_ <=this->storage_len_)
		{
			return 2;
		}
		return 1;
	}
	return 0;
}

/**
 * return
 *  0: write success
 * -1: no enough space error
 */
int MakeMsgBlock::make_block(MessageBlock *mb,int recv_len)
{
	if (NULL == this->base_ptr_)
	{
		return -1;
	}

	if ((this->write_ptr_ - this->base_ptr_ + 1) % this->size_
			== (this->read_ptr_ - this->base_ptr_))
	{ //end,no space
		LOG((ERROR_LOG,"MakeMsgBlock::make_block no space"));
		return -1;
	}
	else
	{
		int left_len = 0;
		bool flag = true;
		if (this->read_ptr_ == this->write_ptr_
				&&(this->base_ptr_+this->size_-this->write_ptr_)>=recv_len)
		{
			left_len = this->size_;
		}
		else if (this->read_ptr_ > this->write_ptr_)
		{
			left_len = this->read_ptr_ - this->write_ptr_;
		}
		else
		{
			left_len = this->size_ - (this->write_ptr_ - this->read_ptr_);
			if (this->base_ptr_ + this->size_ - this->write_ptr_ < recv_len)
			{
				flag = false;
			}
		}
		if (recv_len > left_len) //no enough space
		{
			LOG((ERROR_LOG,"MakeMsgBlock::make_block no enough space[%d][%d]",recv_len,left_len));
			return -1;
		}

		if (flag)
		{
			LOG((DEBUG_LOG,"MakeMsgBlock::make_block [%d][%d]",recv_len,left_len));
			memcpy(this->write_ptr_, mb->rd_ptr(), recv_len);
			this->write_ptr_ += recv_len;
		}
		else
		{
			LOG((DEBUG_LOG,"MakeMsgBlock::make_block [%d][%d] reach end",recv_len,left_len));
			int first_len = this->base_ptr_ + this->size_ - this->write_ptr_;
			memcpy(this->write_ptr_, mb->rd_ptr(), first_len);
			mb->rd_ptr(first_len);
			this->write_ptr_ = this->base_ptr_;
			memcpy(this->write_ptr_, mb->rd_ptr(), recv_len - first_len);
			this->write_ptr_ += (recv_len - first_len);
		}
		this->storage_len_ += recv_len;
	}
	return 0;
}

